package com.xyht.sca_s.student_manage_system.modules.appDynamic.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.config.Configure;
import com.deepoove.poi.data.PictureRenderData;
import com.deepoove.poi.data.PictureType;
import com.deepoove.poi.data.Pictures;
import com.deepoove.poi.policy.PictureRenderPolicy;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.spire.doc.Document;
import com.spire.doc.FileFormat;
import com.xyht.sca_s.student_manage_system.common.commonReturn.CommonCodeEnum;
import com.xyht.sca_s.student_manage_system.common.commonReturn.CommonResult;
import com.xyht.sca_s.student_manage_system.common.commonReturn.ExceptionCast;
import com.xyht.sca_s.student_manage_system.common.commonReturn.ResponseResult;
import com.xyht.sca_s.student_manage_system.common.util.CacheUtil;
import com.xyht.sca_s.student_manage_system.common.util.TimeUtil;
import com.xyht.sca_s.student_manage_system.modules.appDynamic.applicationDelivery.AppDelivery;
import com.xyht.sca_s.student_manage_system.modules.appDynamic.applicationDelivery.req.AppConfirmAndResignReq;
import com.xyht.sca_s.student_manage_system.modules.appDynamic.applicationDelivery.req.AppJobChangeReq;
import com.xyht.sca_s.student_manage_system.modules.appDynamic.applicationDelivery.req.AppOnboardingApplicationReq;
import com.xyht.sca_s.student_manage_system.modules.appDynamic.entity.*;
import com.xyht.sca_s.student_manage_system.modules.appDynamic.entity.req.*;
import com.xyht.sca_s.student_manage_system.modules.appDynamic.entity.resp.*;
import com.xyht.sca_s.student_manage_system.modules.appDynamic.mapper.*;
import com.xyht.sca_s.student_manage_system.modules.appDynamic.service.AppApplicationDataService;
import com.xyht.sca_s.student_manage_system.modules.appDynamic.service.AppProcessNodeDetailService;
import com.xyht.sca_s.student_manage_system.modules.appDynamic.util.AppDynamicUtil;
import com.xyht.sca_s.student_manage_system.modules.common.constant.FileSavePathConstant;
import com.xyht.sca_s.student_manage_system.modules.newTips.entity.SmsNewTips;
import com.xyht.sca_s.student_manage_system.modules.newTips.entity.SmsNewTipsRecord;
import com.xyht.sca_s.student_manage_system.modules.newTips.mapper.SmsNewTipsMapper;
import com.xyht.sca_s.student_manage_system.modules.newTips.mapper.SmsNewTipsRecordMapper;
import com.xyht.sca_s.student_manage_system.modules.newTips.util.NewTipsUtil;
import com.xyht.sca_s.student_manage_system.modules.org.entity.SmsOrgStructure;
import com.xyht.sca_s.student_manage_system.modules.org.mapper.SmsOrgStructureMapper;
import com.xyht.sca_s.student_manage_system.modules.student.entity.SmsStudentInfo;
import com.xyht.sca_s.student_manage_system.modules.student.entity.SmsUserStudentRelation;
import com.xyht.sca_s.student_manage_system.modules.student.mapper.SmsStudentInfoMapper;
import com.xyht.sca_s.student_manage_system.modules.student.mapper.SmsUserStudentRelationMapper;
import com.xyht.sca_s.student_manage_system.modules.user.entity.SmsRole;
import com.xyht.sca_s.student_manage_system.modules.user.entity.SmsUser;
import com.xyht.sca_s.student_manage_system.modules.user.mapper.SmsRoleMapper;
import com.xyht.sca_s.student_manage_system.modules.user.mapper.SmsUserMapper;
import org.apache.commons.lang3.StringUtils;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.font.PDFont;
import org.apache.pdfbox.pdmodel.font.PDType0Font;
import org.apache.pdfbox.pdmodel.graphics.state.PDExtendedGraphicsState;
import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.ss.usermodel.FillPatternType;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.springframework.beans.BeanUtils;
import org.springframework.core.io.ClassPathResource;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.util.*;
import java.util.concurrent.Future;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import static com.xyht.sca_s.student_manage_system.common.util.ParamCheckUtil.isNullOrEmpty;
import static com.xyht.sca_s.student_manage_system.modules.appDynamic.applicationDelivery.AppConstant.*;
import static com.xyht.sca_s.student_manage_system.modules.appDynamic.constant.ApplicationAllowStatusConstant.*;
import static com.xyht.sca_s.student_manage_system.modules.appDynamic.constant.ApplyTypeConstant.*;
import static com.xyht.sca_s.student_manage_system.modules.appDynamic.constant.ApproverTypeConstant.*;
import static com.xyht.sca_s.student_manage_system.modules.appDynamic.constant.ConditionControlSubTypeConstant.CONDITION_SUBTYPE_ORGANIZATION;
import static com.xyht.sca_s.student_manage_system.modules.appDynamic.constant.ConditionControlSubTypeConstant.CONDITION_SUBTYPE_RANGE;
import static com.xyht.sca_s.student_manage_system.modules.appDynamic.constant.NodeAllowTypeConstant.*;
import static com.xyht.sca_s.student_manage_system.modules.appDynamic.constant.NodeTypeConstant.*;
import static com.xyht.sca_s.student_manage_system.modules.appDynamic.constant.NoticeTypeConstant.*;
import static com.xyht.sca_s.student_manage_system.modules.common.constant.FileSavePathConstant.*;
import static com.xyht.sca_s.student_manage_system.modules.student.constant.StudentInfoCommonConstant.PER_SHEET_NUM;

/**
 * <p>
 * 应用用户申请数据表：存放用户申请表数据 服务实现类
 * </p>
 *
 * @author XYHT
 * @since 2023-07-26
 */
@Service
public class AppApplicationDataServiceImpl extends ServiceImpl<AppApplicationDataMapper, AppApplicationData> implements AppApplicationDataService {
    @Resource
    private FileSavePathConstant fileSavePathConstant;
    @Resource
    private AppProcessNodeConfigMapper appProcessNodeConfigMapper;
    @Resource
    private AppApplicationMapper appApplicationMapper;
    @Resource
    private AppApplicationDataMapper appApplicationDataMapper;
    @Resource
    private AppControlOrderMapper appControlOrderMapper;
    @Resource
    private AppProcessNodeDetailService appProcessNodeDetailService;
    @Resource
    private AppProcessNodeDetailMapper appProcessNodeDetailMapper;
    @Resource
    private AppApplicationMpjMapper appApplicationMpjMapper;
    @Resource
    private NewTipsUtil newTipsUtil;
    @Resource
    private CacheUtil cacheUtil;
    @Resource
    private SmsUserMapper smsUserMapper;
    @Resource
    private SmsRoleMapper smsRoleMapper;
    @Resource
    private AppDynamicUtil appDynamicUtil;
    @Resource
    private AppBasicInfoMapper appBasicInfoMapper;
    @Resource
    private AppProcessRollbackRecordMapper appProcessRollbackRecordMapper;
    @Resource
    private AppConditionGroupMapper appConditionGroupMapper;
    @Resource
    private AppConditionMapper appConditionMapper;
    @Resource
    private AppDelivery appDelivery;
    @Resource
    private AppTemplateMapper appTemplateMapper;
    @Resource
    private SmsOrgStructureMapper smsOrgStructureMapper;
    @Resource
    private SmsNewTipsMapper smsNewTipsMapper;
    @Resource
    private SmsNewTipsRecordMapper smsNewTipsRecordMapper;
    @Resource
    private SmsUserStudentRelationMapper smsUserStudentRelationMapper;
    @Resource
    private SmsStudentInfoMapper smsStudentInfoMapper;

    @Override
    @Transactional
    public ResponseResult cancelApplication(ApplicationCancelReq cancelReq, String userId) {
        //校验参数
        if (isNullOrEmpty(cancelReq.getApplicationId())) {
            return CommonResult.failed(CommonCodeEnum.INVALID_PARAM);
        }
        //判断审批表是否存在并且是否为本人
        AppApplication appApplication = appApplicationMapper.selectOne(new LambdaQueryWrapper<AppApplication>()
                .eq(AppApplication::getId, cancelReq.getApplicationId())
                .eq(AppApplication::getUserId, userId));
        if (isNullOrEmpty(appApplication) || appApplication.getId() == null) {
            return CommonResult.failed(CommonCodeEnum.APPLICATION_NOT_EXIST);
        }
        //校验当前申请表状态是否已结束
        if (appApplication.getAllowStatus() == APPLICATION_ALLOW_STATUS_PASS || appApplication.getAllowStatus() == APPLICATION_ALLOW_STATUS_DENY) {
            return CommonResult.failed(CommonCodeEnum.APPLICATION_CANCEL_FAIL_ALLOWING);
        }

        //开始撤回
        //修改申请表状态
        appApplication.setAllowStatus(APPLICATION_ALLOW_STATUS_CANCEL);
        appApplicationMapper.updateById(appApplication);

        //修改节点状态
        AppProcessNodeDetail processNodeDetail = new AppProcessNodeDetail();
        processNodeDetail.setAllowType(NODE_ALLOW_STATUS_CANCELED);
        appProcessNodeDetailMapper.update(processNodeDetail, new LambdaQueryWrapper<AppProcessNodeDetail>()
                .eq(AppProcessNodeDetail::getApplicationId, cancelReq.getApplicationId()));

        //通知
        newTipsUtil.cancelFinishTips(cancelReq.getApplicationId());
        return CommonResult.success();
    }

    @Override
    public ResponseResult getApplicationList(Integer pageSize, Integer pageNum, Integer applyType, String appId, String userId) {
        List<AppApplicationResp> respList = new ArrayList<>();
        int total = 0;
        if (applyType == APP_TYPE_APPLY) {//已发起
            //获取申请表
            Page<AppApplication> page = new Page<>(pageNum, pageSize);
            appApplicationMapper.selectPage(page, new LambdaQueryWrapper<AppApplication>()
                    .eq(AppApplication::getUserId, userId)
                    .eq(AppApplication::getAppId, appId)
                    .orderByDesc(AppApplication::getCreateTime));

            //构造返回数据
            respList = page.getRecords().stream().map(appApplication -> {
                //设置申请表数据
                AppApplicationResp appApplicationResp = new AppApplicationResp();
                BeanUtils.copyProperties(appApplication, appApplicationResp);

                //返回当前申请表数据信息
                List<ApplicationDataResp> dataRespList = appDynamicUtil.getDataRespList(appApplication.getId());
                appApplicationResp.setDataList(dataRespList);

                //设置申请人信息
                SmsUser smsUser = cacheUtil.getUserInfo(appApplication.getUserId());
                AppApplicationUserResp userResp = new AppApplicationUserResp();
                BeanUtils.copyProperties(smsUser, userResp);
                appApplicationResp.setApplyUserInfo(userResp);

                return appApplicationResp;
            }).collect(Collectors.toList());

            total = (int) page.getTotal();
        } else if (applyType == APP_TYPE_ALLOWED) {//已办
            Page<AppApplicationMPJResp> page = new Page<>(pageNum, pageSize);
            MPJLambdaWrapper<AppProcessNodeDetail> mpjLambdaWrapper = new MPJLambdaWrapper<>();
            mpjLambdaWrapper.selectAll(AppProcessNodeDetail.class)
                    .selectAll(AppApplication.class)
                    .selectAs(AppProcessNodeDetail::getCreateTime, "createTime")
                    .selectAs(AppProcessNodeDetail::getId, "detailId")
                    .leftJoin(AppApplication.class, AppApplication::getId, AppProcessNodeDetail::getApplicationId)
                    .eq(AppProcessNodeDetail::getAllowUserId, userId)
                    .eq(AppApplication::getAppId, appId)
                    .eq(AppProcessNodeDetail::getAppId, appId)
                    .and(wrapper ->
                            wrapper.eq(AppProcessNodeDetail::getAllowType, APPLICATION_ALLOW_STATUS_PASS)
                                    .or()
                                    .eq(AppProcessNodeDetail::getAllowType, APPLICATION_ALLOW_STATUS_DENY))
                    .orderByDesc(AppApplication::getCreateTime);

            appApplicationMpjMapper.selectJoinPage(page, AppApplicationMPJResp.class, mpjLambdaWrapper);
            respList = page.getRecords().stream().map(appApplicationMPJResp -> {
                AppApplicationResp appApplicationResp = new AppApplicationResp();
                BeanUtils.copyProperties(appApplicationMPJResp, appApplicationResp);
                appApplicationResp.setAllowStatus(appApplicationMPJResp.getAllowType());
                appApplicationResp.setCreateTime(appApplicationMPJResp.getCreateTime());
                appApplicationResp.setId(appApplicationMPJResp.getApplicationId());
                //设置申请表数据
                List<ApplicationDataResp> dataRespList = appDynamicUtil.getDataRespList(appApplicationMPJResp.getApplicationId());
                appApplicationResp.setDataList(dataRespList);
                //设置申请人信息
                SmsUser applyUser = cacheUtil.getUserInfo(appApplicationResp.getUserId());
                AppApplicationUserResp userResp = new AppApplicationUserResp();
                if (applyUser != null) {
                    BeanUtils.copyProperties(applyUser, userResp);
                }
                appApplicationResp.setApplyUserInfo(userResp);
                //设置审批人信息
                SmsUser allowUser = cacheUtil.getUserInfo(appApplicationResp.getAllowUserId());
                AppApplicationUserResp staffUserDTO = new AppApplicationUserResp();
                if (allowUser != null) {
                    BeanUtils.copyProperties(allowUser, staffUserDTO);
                }
                appApplicationResp.setAllowUserInfo(staffUserDTO);

                return appApplicationResp;
            }).collect(Collectors.toList());

            total = (int) page.getTotal();
        } else if (applyType == APP_TYPE_ALLOW_WAIT_ME) {//待办
            //获取所有待审批申请流程节点
            List<AppProcessNodeDetail> detailList = appProcessNodeDetailMapper.selectList(new LambdaQueryWrapper<AppProcessNodeDetail>()
                    .eq(AppProcessNodeDetail::getAppId, appId)
                    .eq(AppProcessNodeDetail::getAllowType, APPLICATION_ALLOW_STATUS_WAIT_ALLOW)
                    .eq(AppProcessNodeDetail::getType, APPROVE_NODE));
            if (detailList.isEmpty()) {
                return CommonResult.success(respList, total);
            }
            //获取当前用户角色id集合
            List<String> roleIdList = cacheUtil.getRoleList(userId);
            List<AppProcessNodeDetail> filterDetailList = detailList.stream().filter(detail -> {
                //审批对象id为 null 排除
                if (isNullOrEmpty(detail.getApproverObjectId())) {
                    return false;
                }
                //审批人不为本人的排除
                if (detail.getApproverType() == DYNAMIC_APP_ASSIGNER) {//指定人
                    if (!detail.getApproverObjectId().equals(userId)) {
                        return false;
                    }
                } else {//指定角色
                    List<String> filterRoleList = roleIdList.stream().filter(roleId -> roleId.equals(detail.getApproverObjectId())).collect(Collectors.toList());
                    if (filterRoleList.size() < 1) {
                        return false;
                    }
                }
                //上一个节点状态不为“通过”的排除
                AppProcessNodeDetail prevNodeDetail = appDynamicUtil.getPrevNodeDetail(detail);
                if (isNullOrEmpty(prevNodeDetail) || prevNodeDetail.getAllowType() != NODE_ALLOW_STATUS_PASS) {
                    return false;
                }
                return true;
            }).collect(Collectors.toList());
            if (filterDetailList.isEmpty()) {
                return CommonResult.success(respList, total);
            }
            //收集节点id
            List<String> detailIdList = filterDetailList.stream().map(AppProcessNodeDetail::getId).collect(Collectors.toList());
            Page<AppProcessNodeDetail> page = new Page<>(pageNum, pageSize);
            appProcessNodeDetailMapper.selectPage(page, new LambdaQueryWrapper<AppProcessNodeDetail>()
                    .in(AppProcessNodeDetail::getId, detailIdList)
                    .orderByDesc(AppProcessNodeDetail::getCreateTime));

            respList = page.getRecords().stream().map(appApplicationDetail -> {
                AppApplication application = appApplicationMapper.selectById(appApplicationDetail.getApplicationId());
                if (isNullOrEmpty(application)) {
                    return null;
                }
                AppApplicationResp applicationResp = new AppApplicationResp();
                BeanUtils.copyProperties(application, applicationResp);
                applicationResp.setId(appApplicationDetail.getApplicationId());
                //设置申请表数据
                List<ApplicationDataResp> dataRespList = appDynamicUtil.getDataRespList(appApplicationDetail.getApplicationId());
                applicationResp.setDataList(dataRespList);
                //设置申请人信息
                SmsUser applyUser = cacheUtil.getUserInfo(applicationResp.getUserId());
                AppApplicationUserResp applicationUserResp = new AppApplicationUserResp();
                if (applyUser != null) {
                    BeanUtils.copyProperties(applyUser, applicationUserResp);
                }
                applicationResp.setApplyUserInfo(applicationUserResp);
                applicationResp.setDetailId(appApplicationDetail.getId());
                return applicationResp;
            }).filter(Objects::nonNull).collect(Collectors.toList());

            total = (int) page.getTotal();
        } else if (applyType == APP_TYPE_COPY) {//抄送我
            List<String> roleIdList = cacheUtil.getRoleList(userId);
            //获取所有待审批申请流程节点
            List<AppProcessNodeDetail> detailList = appProcessNodeDetailMapper.selectList(new LambdaQueryWrapper<AppProcessNodeDetail>()
                    .eq(AppProcessNodeDetail::getAppId, appId)
                    .eq(AppProcessNodeDetail::getAllowType, APPLICATION_ALLOW_STATUS_PASS)
                    .eq(AppProcessNodeDetail::getType, COPY_NODE));
            List<AppProcessNodeDetail> filterDetailList = detailList.stream().filter(detail -> {
                //审批对象id为 null 排除
                if (isNullOrEmpty(detail.getApproverObjectId())) {
                    return false;
                }
                //抄送人不为本人的排除
                if (detail.getApproverType() == DYNAMIC_APP_ASSIGNER) {
                    if (!detail.getApproverObjectId().equals(userId)) {
                        return false;
                    }
                } else {
                    List<String> filterRoleList = roleIdList.stream().filter(roleId -> roleId.equals(detail.getApproverObjectId())).collect(Collectors.toList());
                    if (filterRoleList.size() < 1) {
                        return false;
                    }
                }
                //上一个节点状态不为“通过”的排除
                AppProcessNodeDetail prevNodeDetail = appDynamicUtil.getPrevNodeDetail(detail);
                if (isNullOrEmpty(prevNodeDetail) || prevNodeDetail.getAllowType() != NODE_ALLOW_STATUS_PASS) {
                    return false;
                }
                return true;
            }).collect(Collectors.toList());
            if (filterDetailList.isEmpty()) {
                return CommonResult.success(respList, total);
            }
            //收集节点id
            List<String> detailIdList = filterDetailList.stream().map(AppProcessNodeDetail::getId).collect(Collectors.toList());
            Page<AppProcessNodeDetail> page = new Page<>(pageNum, pageSize);
            appProcessNodeDetailMapper.selectPage(page, new LambdaQueryWrapper<AppProcessNodeDetail>()
                    .in(AppProcessNodeDetail::getId, detailIdList)
                    .orderByDesc(AppProcessNodeDetail::getCreateTime));

            respList = page.getRecords().stream().map(appApplicationDetail -> {
                AppApplication application = appApplicationMapper.selectById(appApplicationDetail.getApplicationId());
                AppApplicationResp applicationResp = new AppApplicationResp();
                BeanUtils.copyProperties(application, applicationResp);
                applicationResp.setId(appApplicationDetail.getApplicationId());
                //设置申请表数据
                List<ApplicationDataResp> dataRespList = appDynamicUtil.getDataRespList(appApplicationDetail.getApplicationId());
                applicationResp.setDataList(dataRespList);
                //设置申请人信息
                SmsUser applyUser = cacheUtil.getUserInfo(applicationResp.getUserId());
                AppApplicationUserResp applicationUserResp = new AppApplicationUserResp();
                if (applyUser != null) {
                    BeanUtils.copyProperties(applyUser, applicationUserResp);
                }
                applicationResp.setApplyUserInfo(applicationUserResp);
                applicationResp.setDetailId(appApplicationDetail.getId());
                return applicationResp;
            }).collect(Collectors.toList());
            total = (int) page.getTotal();
        }

        return CommonResult.success(respList, total);
    }

    @Override
    public ResponseResult getApplicationDetail(String applicationId, String appId, String userId) {
        //校验应用是否存在
        AppBasicInfo app = appBasicInfoMapper.selectById(appId);
        if (isNullOrEmpty(app)) {
            return CommonResult.failed(CommonCodeEnum.DYNAMIC_APP_NOT_EXIST);
        }
        //校验申请表是否存在
        AppApplication appApplication = appApplicationMapper.selectById(applicationId);
        if (isNullOrEmpty(appApplication)) {
            return CommonResult.failed(CommonCodeEnum.APPLICATION_NOT_EXIST);
        }

        //构造返回数据
        //设置申请表信息
        ApplicationDetailResp applicationDetailResp = new ApplicationDetailResp();
        BeanUtils.copyProperties(appApplication, applicationDetailResp);
        //设置申请人信息
        SmsUser smsUser = cacheUtil.getUserInfo(applicationDetailResp.getUserId());
        AppApplicationUserResp userResp = new AppApplicationUserResp();
        if (!isNullOrEmpty(smsUser)) {
            BeanUtils.copyProperties(smsUser, userResp);
        }
        applicationDetailResp.setApplyUserInfo(userResp);

        //设置申请表数据
        List<ApplicationDataResp> dataRespList = appDynamicUtil.getDataRespList(applicationId);
        applicationDetailResp.setDataList(dataRespList);

        //设置流程
        List<AppProcessNodeDetail> nodeDetailList = appProcessNodeDetailMapper.selectList(new LambdaQueryWrapper<AppProcessNodeDetail>()
                .eq(AppProcessNodeDetail::getApplicationId, applicationId)
                .eq(AppProcessNodeDetail::getAppId, appId)
                .orderByAsc(AppProcessNodeDetail::getSort));
        if (nodeDetailList.isEmpty()) {
            return CommonResult.failed(CommonCodeEnum.NODE_DETAIL_LIST_NOT_EXIST);
        }
        List<ApplicationNodeDetailResp> nodeDetailRespList = nodeDetailList.stream().map(nodeDetail -> {
            ApplicationNodeDetailResp applicationNodeDetailResp = new ApplicationNodeDetailResp();
            BeanUtils.copyProperties(nodeDetail, applicationNodeDetailResp);
            applicationNodeDetailResp.setAuthority(JSONObject.parseArray(nodeDetail.getAuthority(), AuthorityResp.class));

            //设置角色名
            if (nodeDetail.getApproverType() == DYNAMIC_APP_ROLE) {
                SmsRole role = smsRoleMapper.selectById(nodeDetail.getApproverObjectId());
                applicationNodeDetailResp.setObjectName(role.getRoleName());
            } else {
                //设置审批人信息
                SmsUser userInfo = cacheUtil.getUserInfo(nodeDetail.getApproverObjectId());
                if (!isNullOrEmpty(userInfo)) {
                    AppApplicationUserResp allowUserInfo = new AppApplicationUserResp();
                    BeanUtils.copyProperties(userInfo, allowUserInfo);
                    applicationNodeDetailResp.setAllowUserInfo(allowUserInfo);
                }
            }

            return applicationNodeDetailResp;
        }).collect(Collectors.toList());
        applicationDetailResp.setNodeDetailList(nodeDetailRespList);

        return CommonResult.success(applicationDetailResp);
    }

    @Override
    @Transactional
    public ResponseResult allowApplication(ApplicationAllowReq allowReq, String userId) {
        //校验前端参数
        if (isNullOrEmpty(allowReq.getProcessNodeDetailId(), allowReq.getApplicationId(), allowReq.getAllowType())) {
            return CommonResult.failed(CommonCodeEnum.INVALID_PARAM);
        }
        //若传入参数非“通过”且非“驳回”,进行阻断
        if (allowReq.getAllowType() != NODE_ALLOW_STATUS_PASS && allowReq.getAllowType() != NODE_ALLOW_STATUS_DENY) {
            return CommonResult.failed(CommonCodeEnum.INVALID_PARAM);
        }
        //校验申请表是否存在
        AppApplication application = appApplicationMapper.selectById(allowReq.getApplicationId());
        if (application == null || isNullOrEmpty(application.getId())) {
            return CommonResult.failed(CommonCodeEnum.APPLICATION_NOT_EXIST);
        }
        //校验该节点 是否为“待审批”状态 和 审批人类型是否正常
        AppProcessNodeDetail nodeDetail = appProcessNodeDetailMapper.selectById(allowReq.getProcessNodeDetailId());
        if (isNullOrEmpty(nodeDetail) || nodeDetail.getAllowType() != NODE_ALLOW_STATUS_WAIT_ALLOW ||
                (nodeDetail.getApproverType() != DYNAMIC_APP_ROLE && nodeDetail.getApproverType() != DYNAMIC_APP_ASSIGNER)) {
            return CommonResult.failed(CommonCodeEnum.PROCESS_NODE_DETAIL_ALLOW_FAIL);
        }
        //判断该节点是否本人审批(防止跳节点审批)
        if (nodeDetail.getApproverType() == DYNAMIC_APP_ROLE) {
            List<String> roleIdList = cacheUtil.getRoleList(userId);
            List<String> filterRoleList = roleIdList.stream().filter(roleId -> roleId.equals(nodeDetail.getApproverObjectId())).collect(Collectors.toList());
            if (filterRoleList.isEmpty()) {
                return CommonResult.failed(CommonCodeEnum.PROCESS_NODE_DETAIL_ALLOW_FAIL);
            }
        } else {
            if (!nodeDetail.getApproverObjectId().equals(userId)) {
                return CommonResult.failed(CommonCodeEnum.PROCESS_NODE_DETAIL_ALLOW_FAIL);
            }
        }

        //校验上一个节点状态是否为“通过”
        AppProcessNodeDetail prevNodeDetail = appDynamicUtil.getPrevNodeDetail(nodeDetail);
        if (isNullOrEmpty(prevNodeDetail) || prevNodeDetail.getAllowType() != NODE_ALLOW_STATUS_PASS) {
            return CommonResult.failed(CommonCodeEnum.PROCESS_NODE_DETAIL_ALLOW_FAIL);
        }

        //修改ProcessDetail节点表
        AppProcessNodeDetail node = new AppProcessNodeDetail();
        node.setId(allowReq.getProcessNodeDetailId());
        node.setAllowType(allowReq.getAllowType());
        node.setAllowUserId(userId);
        node.setAllowTime(new Date());
        //设置签名
        if (nodeDetail.getIsSign()) {
            if (isNullOrEmpty(allowReq.getSign())) {
                return CommonResult.failed(CommonCodeEnum.INVALID_PARAM);
            }
            node.setSign(allowReq.getSign());
        }
        //设置印章
//        if (nodeDetail.getIsStamp()) {
//            if (isNullOrEmpty(allowReq.getStamp())) {
//                return CommonResult.failed(CommonCodeEnum.INVALID_PARAM);
//            }
//            node.setStamp(allowReq.getStamp());
//        }
        //设置审批意见
        if (nodeDetail.getIsOpinion()) {
            if (isNullOrEmpty(allowReq.getOpinion())) {
                return CommonResult.failed(CommonCodeEnum.INVALID_PARAM);
            }
            node.setOpinion(allowReq.getOpinion());
        }
        appProcessNodeDetailMapper.updateById(node);

        //流程是否结束标志
        boolean processIsEnd = false;
        //判断是否最后一个节点/是否驳回
        if (appDynamicUtil.getNextNodeDetail(nodeDetail) == null || allowReq.getAllowType() == NODE_ALLOW_STATUS_DENY) {
            //流程结束
            processIsEnd = true;
            //修改申请表
            AppApplication appApplication = new AppApplication();
            appApplication.setId(allowReq.getApplicationId());
            appApplication.setAllowStatus(allowReq.getAllowType());
            appApplicationMapper.updateById(appApplication);

            //节点驳回 重置其余节点状态
            if (allowReq.getAllowType() == NODE_ALLOW_STATUS_DENY) {
                AppProcessNodeDetail updateDenyDetail = new AppProcessNodeDetail();
                updateDenyDetail.setAllowType(NODE_ALLOW_STATUS_DENIED);
                appProcessNodeDetailMapper.update(updateDenyDetail, new LambdaQueryWrapper<AppProcessNodeDetail>()
                        .eq(AppProcessNodeDetail::getApplicationId, allowReq.getApplicationId())
                        .eq(AppProcessNodeDetail::getAllowType, NODE_ALLOW_STATUS_WAIT_ALLOW));
            }
        }

        //消息通知
        if (processIsEnd) {//流程结束
            boolean allowResult = false;//流程状态
            if (allowReq.getAllowType() == NODE_ALLOW_STATUS_PASS) {//通过
                allowResult = true;

                //特殊应用流程完毕后进行特殊操作
                //获取应用信息
                AppBasicInfo appBasicInfo = appBasicInfoMapper.selectById(application.getAppId());
                specialOperationsAfterCompleting(appBasicInfo.getAppName(), application, userId);
            }
            newTipsUtil.allowFinishTips(allowReq.getApplicationId(), userId, allowResult);
        } else {
            //消息通知
            //向后通知流程节点
            AppProcessNodeDetail nextNode = appDynamicUtil.getNextNodeDetail(nodeDetail);
            appDynamicUtil.notice(nextNode, userId, allowReq.getApplicationId(), ALLOW_APPLICATION_NOTICE);
        }
        return CommonResult.success();
    }

    /**
     * 特殊应用流程完毕后进行特殊操作
     *
     * @param appName     应用名称
     * @param application 应用申请表
     * @param userId      当前用户id
     */
    public void specialOperationsAfterCompleting(String appName, AppApplication application, String userId) {
        switch (appName) {
            case APP_JOB_CHANGE_NAME: //岗位变动
                AppJobChangeReq jobChangeReq = new AppJobChangeReq();
                int[] jobChangeControlSort = {1};
                //获取申请表数据
                List<ApplicationDataResp> jobChangeDataRespList = appDynamicUtil.getDataRespList(application.getId(), jobChangeControlSort);
                //设置传入数据
                jobChangeReq.setUserId(userId);
                jobChangeReq.setTchOrgId(jobChangeDataRespList.get(0).getData());
                //进行其他操作
                appDelivery.appDelivery(JSON.toJSONString(jobChangeReq), APP_JOB_CHANGE);
                break;
            case APP_ONBOARDING_APPLICATION_NAME: //新员工入职申请
                AppOnboardingApplicationReq onboardingApplicationReq = new AppOnboardingApplicationReq();
                int[] onboardingControlSort = {1, 2, 3, 4, 5};
                //获取申请表数据
                List<ApplicationDataResp> onboardingDataRespList = appDynamicUtil.getDataRespList(application.getId(), onboardingControlSort);
                //设置传入数据
                onboardingApplicationReq.setTchOrg(onboardingDataRespList.get(0).getData());
                onboardingApplicationReq.setTchName(onboardingDataRespList.get(1).getData());
                onboardingApplicationReq.setTchSex(onboardingDataRespList.get(2).getData());
                onboardingApplicationReq.setTchTel(onboardingDataRespList.get(3).getData());
                onboardingApplicationReq.setTchIdCard(onboardingDataRespList.get(4).getData());
                //进行其他操作
                appDelivery.appDelivery(JSON.toJSONString(onboardingApplicationReq), APP_ONBOARDING_APPLICATION);
                break;
            case APP_CONFIRMATION_APPLICATION_NAME: {//转正申请
                AppConfirmAndResignReq confirmAndResignReq = new AppConfirmAndResignReq();
                int[] confirmationControlSort = {1};
                //获取申请表数据
                List<ApplicationDataResp> confirmationDataRespList = appDynamicUtil.getDataRespList(application.getId(), confirmationControlSort);
                confirmAndResignReq.setUserId(confirmationDataRespList.get(0).getData());
                //进行其他操作
                appDelivery.appDelivery(JSON.toJSONString(confirmAndResignReq), APP_CONFIRMATION_APPLICATION);
                break;
            }
            case APP_RESIGNATION_APPLICATION_NAME: {//离职申请
                AppConfirmAndResignReq confirmAndResignReq = new AppConfirmAndResignReq();
                int[] resignationControlSort = {1};
                //获取申请表数据
                List<ApplicationDataResp> resignationDataRespList = appDynamicUtil.getDataRespList(application.getId(), resignationControlSort);
                confirmAndResignReq.setUserId(resignationDataRespList.get(0).getData());
                //进行其他操作
                appDelivery.appDelivery(JSON.toJSONString(confirmAndResignReq), APP_RESIGNATION_APPLICATION);
                break;
            }
            case APP_STUDENT_BACK_SCHOOL_APPLICATION_NAME: // 复学申请
            case APP_STUDENT_SUSPENSION_STUDY_APPLICATION_NAME: // 休学申请
            case APP_STUDENT_LEAVE_SCHOOL_APPLICATION_NAME: // 退学申请
            case APP_NEW_STUDENT_CONSCRIPTION_APPLICATION_NAME: // 新生应征入伍保留入学资格申请
            case APP_OLD_STUDENT_CONSCRIPTION_APPLICATION_NAME: { // 学生参军入伍保留学籍申请
                List<SmsUserStudentRelation> userStudentRelationList = smsUserStudentRelationMapper.selectList(new LambdaQueryWrapper<SmsUserStudentRelation>()
                        .eq(SmsUserStudentRelation::getUserId, application.getUserId()));
                SmsStudentInfo studentInfo = smsStudentInfoMapper.selectById(userStudentRelationList.get(0).getStuId());
                if (studentInfo == null) {
                    ExceptionCast.cast(CommonResult.failed(CommonCodeEnum.STUDENT_NOT_EXIST));
                }

                // 修改学生状态
                studentInfo.setStuStatus(getStuDentStatusByAppName(appName));
                smsStudentInfoMapper.updateById(studentInfo);
                break;
            }
            default:
                break;
        }
    }

    /**
     * 根据应用名获取学生状态
     *
     * @param appName 应用名
     * @return 学生状态
     */
    public String getStuDentStatusByAppName(String appName) {
        switch (appName) {
            case APP_STUDENT_BACK_SCHOOL_APPLICATION_NAME: // 复学申请
                return APP_STUDENT_BACK_SCHOOL_APPLICATION_STUDENT_STATUS;
            case APP_STUDENT_SUSPENSION_STUDY_APPLICATION_NAME: // 休学申请
                return APP_STUDENT_SUSPENSION_STUDY_APPLICATION_STUDENT_STATUS;
            case APP_STUDENT_LEAVE_SCHOOL_APPLICATION_NAME: // 退学申请
                return APP_STUDENT_LEAVE_SCHOOL_APPLICATION_STUDENT_STATUS;
            case APP_NEW_STUDENT_CONSCRIPTION_APPLICATION_NAME: // 新生应征入伍保留入学资格申请
                return APP_NEW_STUDENT_CONSCRIPTION_APPLICATION_STUDENT_STATUS;
            case APP_OLD_STUDENT_CONSCRIPTION_APPLICATION_NAME: // 学生参军入伍保留学籍申请
                return APP_OLD_STUDENT_CONSCRIPTION_APPLICATION_STUDENT_STATUS;
            default:
                return "0";
        }
    }

    @Override
    @Transactional
    public ResponseResult transmitApplication(ApplicationTransmitReq transmitReq, String userId) {
        //校验前端参数
        if (isNullOrEmpty(transmitReq.getProcessNodeDetailId(), transmitReq.getUserId(), transmitReq.getApplicationId())) {
            return CommonResult.failed(CommonCodeEnum.INVALID_PARAM);
        }
        //校验是否转给本人
        if (transmitReq.getUserId().equals(userId)) {
            return CommonResult.failed(CommonCodeEnum.TRANSFEROR_CANNOT_BE_IN_PERSON);
        }
        //校验转交用户是否存在
        SmsUser user = smsUserMapper.selectById(transmitReq.getUserId());
        if (isNullOrEmpty(user)) {
            return CommonResult.failed(CommonCodeEnum.PROCESS_NODE_DETAIL_TRANSMIT_USER_NOT_EXIST);
        }
        //校验申请表是否存在
        AppApplication application = appApplicationMapper.selectById(transmitReq.getApplicationId());
        if (isNullOrEmpty(application.getId())) {
            return CommonResult.failed(CommonCodeEnum.APPLICATION_NOT_EXIST);
        }
        //校验该节点是否为“待审批”状态
        AppProcessNodeDetail nodeDetail = appProcessNodeDetailMapper.selectById(transmitReq.getProcessNodeDetailId());
        if (isNullOrEmpty(nodeDetail.getId()) || nodeDetail.getAllowType() != NODE_ALLOW_STATUS_WAIT_ALLOW) {
            return CommonResult.failed(CommonCodeEnum.PROCESS_NODE_DETAIL_TRANSMIT_FAIL);
        }
        //判断该节点是否本人审批(防止跳节点审批)
        if (nodeDetail.getApproverType() == DYNAMIC_APP_ROLE) {
            List<String> roleIdList = cacheUtil.getRoleList(userId);
            List<String> filterRoleList = roleIdList.stream().filter(roleId -> roleId.equals(nodeDetail.getApproverObjectId())).collect(Collectors.toList());
            if (filterRoleList.isEmpty()) {
                return CommonResult.failed(CommonCodeEnum.PROCESS_NODE_DETAIL_TRANSMIT_FAIL);
            }
        } else {
            if (!nodeDetail.getApproverObjectId().equals(userId)) {
                return CommonResult.failed(CommonCodeEnum.PROCESS_NODE_DETAIL_TRANSMIT_FAIL);
            }
        }
        //校验上一个节点状态是否为“通过”
        AppProcessNodeDetail prevNodeDetail = appDynamicUtil.getPrevNodeDetail(nodeDetail);
        if (isNullOrEmpty(prevNodeDetail) || prevNodeDetail.getAllowType() != NODE_ALLOW_STATUS_PASS) {
            return CommonResult.failed(CommonCodeEnum.PROCESS_NODE_DETAIL_ALLOW_FAIL);
        }

        //修改ProcessDetail节点表
        AppProcessNodeDetail node = new AppProcessNodeDetail();
        node.setId(transmitReq.getProcessNodeDetailId());
        node.setApproverType(DYNAMIC_APP_ASSIGNER);
        node.setApproverObjectId(transmitReq.getUserId());
        appProcessNodeDetailMapper.updateById(node);

        appDynamicUtil.newTips(nodeDetail, null, transmitReq.getApplicationId(), TRANS_OR_BACK_APPLICATION_NOTICE);

        return CommonResult.success();
    }

    @Override
    @Transactional
    public ResponseResult backApplication(ApplicationBackReq backReq, String userId) {
        //校验参数
        if (isNullOrEmpty(backReq.getProcessNodeDetailId(), backReq.getApplicationId())) {
            return CommonResult.failed(CommonCodeEnum.INVALID_PARAM);
        }
        //校验申请表是否存在
        AppApplication application = appApplicationMapper.selectById(backReq.getApplicationId());
        if (isNullOrEmpty(application) || application.getId() == null) {
            return CommonResult.failed(CommonCodeEnum.APPLICATION_NOT_EXIST);
        }
        //校验该节点是否为“待审批”状态
        AppProcessNodeDetail nodeDetail = appProcessNodeDetailMapper.selectById(backReq.getProcessNodeDetailId());
        if (nodeDetail == null || isNullOrEmpty(nodeDetail.getId()) || nodeDetail.getAllowType() != NODE_ALLOW_STATUS_WAIT_ALLOW) {
            return CommonResult.failed(CommonCodeEnum.PROCESS_NODE_DETAIL_BACK_FAIL);
        }
        //校验审批节点是否为本人审批
        if (nodeDetail.getApproverType() == DYNAMIC_APP_ROLE) {
            List<String> roleList = cacheUtil.getRoleList(userId);
            List<String> filterRoleList = roleList.stream().filter(role -> role.equals(nodeDetail.getApproverObjectId())).collect(Collectors.toList());
            if (filterRoleList.isEmpty()) {
                return CommonResult.failed(CommonCodeEnum.PROCESS_NODE_DETAIL_BACK_FAIL);
            }
        } else {
            if (!nodeDetail.getApproverObjectId().equals(userId)) {
                return CommonResult.failed(CommonCodeEnum.PROCESS_NODE_DETAIL_BACK_FAIL);
            }
        }

        //获取上一个审批节点,并校验状态是否为“通过”
        AppProcessNodeDetail prevApproveNode = nodeDetail;
        List<String> nodeDetailId = new ArrayList<>();
        while (true) {
            prevApproveNode = appDynamicUtil.getPrevNodeDetail(prevApproveNode);
            nodeDetailId.add(prevApproveNode.getId());
            if (prevApproveNode.getType() == APPROVE_NODE) {
                break;
            } else if (prevApproveNode.getAllowType() != NODE_ALLOW_STATUS_PASS) {
                return CommonResult.failed(CommonCodeEnum.PROCESS_NODE_DETAIL_ALLOW_FAIL);
            } else if (prevApproveNode.getType() == APPLICANT_NODE) {
                return CommonResult.failed(CommonCodeEnum.PROCESS_NODE_DETAIL_BACK_APPLICANT_FAIL);
            }
        }

        //创建回退记录
        AppProcessRollbackRecord rollbackRecord = new AppProcessRollbackRecord();
        rollbackRecord.setAppId(application.getAppId());
        rollbackRecord.setProcessId(backReq.getProcessNodeDetailId());
        rollbackRecord.setTargetProcessId(prevApproveNode.getId());
        rollbackRecord.setReason(backReq.getReason());
        appProcessRollbackRecordMapper.insert(rollbackRecord);

        //开始回退修改前面节点的审批状态
        //修改前面节点(前面节点: 当前节点一直到前面的第一个审批节点)
        AppProcessNodeDetail detail = new AppProcessNodeDetail();
        detail.setAllowType(NODE_ALLOW_STATUS_WAIT_ALLOW);
        appProcessNodeDetailMapper.update(detail, new LambdaQueryWrapper<AppProcessNodeDetail>()
                .in(AppProcessNodeDetail::getId, nodeDetailId));
        //修改当前节点
        nodeDetail.setRollbackId(rollbackRecord.getId());
        appProcessNodeDetailMapper.updateById(nodeDetail);

        //通知
        appDynamicUtil.newTips(prevApproveNode, null, prevApproveNode.getApplicationId(), TRANS_OR_BACK_APPLICATION_NOTICE);

        return CommonResult.success();
    }

    @Override
    public ResponseResult submitApplicationData(SubmitApplicationDataReq req, String userId) {
        //校验参数
        if (isNullOrEmpty(req.getAppId(), req.getDataReqList())) {
            return CommonResult.failed(CommonCodeEnum.INVALID_PARAM);
        }
        //校验 数据数量 与 控件数量 是否匹配
        List<String> controlOrderIdList = req.getDataReqList().stream().map(ApplicationDataReq::getControlOrderId).collect(Collectors.toList());
        Integer controlNum = appControlOrderMapper.selectCount(new LambdaQueryWrapper<AppControlOrder>()
                .eq(AppControlOrder::getAppId, req.getAppId())
                .in(AppControlOrder::getId, controlOrderIdList));
        if (controlNum != req.getDataReqList().size()) {
            return CommonResult.failed(CommonCodeEnum.DATA_NUM_NOT_EQUALS_CONTROL_NUM);
        }
        //校验必填控件是否全部填写
        for (ApplicationDataReq dataReq : req.getDataReqList()) {
            if (isNullOrEmpty(dataReq.getControlOrderId())) {
                return CommonResult.failed(CommonCodeEnum.INVALID_PARAM);
            }
            Boolean isRequired = appDynamicUtil.getIsRequired(dataReq.getControlOrderId());
            if (isRequired == null) {
                return CommonResult.failed(CommonCodeEnum.CONTROL_NOT_EXIST);
            }
            if (isRequired) {
                if (isNullOrEmpty(dataReq.getData())) {
                    return CommonResult.failed(CommonCodeEnum.REQUIRED_CONTROL_NOT_FILL);
                }
            }
        }

        List<AppProcessNodeConfigResp> processNodeRespList = new ArrayList<>();
        //获取申请人节点配置
        AppProcessNodeConfig nodeConfig = appProcessNodeConfigMapper.selectList(new LambdaQueryWrapper<AppProcessNodeConfig>()
                .eq(AppProcessNodeConfig::getAppId, req.getAppId())
                .eq(AppProcessNodeConfig::getType, APPLICANT_NODE)).get(0);
        //记录节点顺序
        int sort = 0;
        while (nodeConfig.getType() != END_NODE) {
            //添加当前节点
            AppProcessNodeConfigResp nodeConfigResp = new AppProcessNodeConfigResp();
            BeanUtils.copyProperties(nodeConfig, nodeConfigResp);
            //设置节点顺序
            nodeConfigResp.setSort(sort++);
            //设置审批人信息
            if (nodeConfigResp.getApproverType() == DYNAMIC_APP_ROLE) {
                SmsRole role = smsRoleMapper.selectById(nodeConfigResp.getApproverObjectId());
                nodeConfigResp.setObjectName(role.getRoleName());
            } else if (nodeConfigResp.getApproverType() == DYNAMIC_APP_ASSIGNER) {
                SmsUser user = smsUserMapper.selectById(nodeConfigResp.getApproverObjectId());
                AppSearchUserResp userResp = new AppSearchUserResp();
                BeanUtils.copyProperties(user, userResp);
                nodeConfigResp.setUserInfo(userResp);
            } else if (nodeConfigResp.getApproverType() == DYNAMIC_APP_SUBMITTER) {
                SmsUser user = smsUserMapper.selectById(userId);
                AppSearchUserResp userResp = new AppSearchUserResp();
                BeanUtils.copyProperties(user, userResp);
                nodeConfigResp.setUserInfo(userResp);
                nodeConfigResp.setApproverType(DYNAMIC_APP_ASSIGNER);
                nodeConfigResp.setApproverObjectId(userId);
            }
            //设置权限
            nodeConfigResp.setAuthority(JSONObject.parseArray(nodeConfig.getAuthority(), AuthorityResp.class));
            //添加本节点
            processNodeRespList.add(nodeConfigResp);

            //节点向后传递
            List<AppProcessNodeConfig> nextNodeList = appDynamicUtil.getNextNodeList(nodeConfig);
            a:
            for (AppProcessNodeConfig nextNode : nextNodeList) {
                while (true) {
                    if (nextNode.getType() == COPY_NODE || nextNode.getType() == APPROVE_NODE || nextNode.getType() == END_NODE) {
                        nodeConfig = nextNode;
                        break a;
                    } else if (nextNode.getType() == DUMMY_NODE) {
                        List<String> nextNodeSonIdList = JSON.parseArray(nextNode.getNextId(), String.class);
                        //虚拟节点(发散节点)
                        if (nextNodeSonIdList.size() > 1) {
                            for (int priority = 1; priority <= nextNodeSonIdList.size(); priority++) {
                                //根据 前节点id 及 优先级 检索到条件组集合
                                List<AppConditionGroup> conditionGroupList = appConditionGroupMapper.selectList(new LambdaQueryWrapper<AppConditionGroup>()
                                        .eq(AppConditionGroup::getPrevNodeId, nextNode.getId())
                                        .eq(AppConditionGroup::getPriority, priority));
                                //校验条件组是否存在
                                if (conditionGroupList.isEmpty()) {
                                    return CommonResult.failed(CommonCodeEnum.APP_CONDITION_GROUP_NOT_EXIST);
                                }
                                //若当前条件组为默认条件
                                if (priority == nextNodeSonIdList.size() && conditionGroupList.get(0).getIsDefault()) {
                                    AppProcessNodeConfig node = appProcessNodeConfigMapper.selectById(conditionGroupList.get(0).getNodeId());
                                    nodeConfig = appDynamicUtil.getNextNode(node);
                                    break a;
                                }
                                //遍历条件组中的条件是否完成
                                for (AppConditionGroup conditionGroup : conditionGroupList) {
                                    List<AppCondition> conditionList = appConditionMapper.selectList(new LambdaQueryWrapper<AppCondition>()
                                            .eq(AppCondition::getConditionGroupId, conditionGroup.getId()));
                                    //校验条件是否存在
                                    if (conditionList.isEmpty()) {
                                        return CommonResult.failed(CommonCodeEnum.APP_CONDITION_NOT_EXIST);
                                    }
                                    //校验条件集合是否全部符合
                                    boolean isReachedConditionList = appDynamicUtil.getIsReachedConditionList(req.getDataReqList(), conditionList, userId);
                                    //若 条件组中 有 条件集合全部符合
                                    if (isReachedConditionList) {
                                        AppProcessNodeConfig node = appProcessNodeConfigMapper.selectById(conditionGroupList.get(0).getNodeId());
                                        nodeConfig = appDynamicUtil.getNextNode(node);
                                        break a;
                                    }
                                }
                            }
                        } else {//虚拟节点(聚合节点)
                            nextNode = appDynamicUtil.getNextNodeList(nextNode).get(0);
                        }
                    }
                }
            }
        }

        return CommonResult.success(processNodeRespList, processNodeRespList.size());
    }

    @Override
    @Transactional
    public ResponseResult submitApplicationProcess(SubmitApplicationProcessReq req, String userId) {
        //校验参数
        if (isNullOrEmpty(req.getAppId(), req.getDataReqList(), req.getNodeList())) {
            return CommonResult.failed(CommonCodeEnum.INVALID_PARAM);
        }
        //校验 数据数量 与 控件数量 是否匹配
        List<String> controlOrderIdList = req.getDataReqList().stream().map(ApplicationDataReq::getControlOrderId).collect(Collectors.toList());
        Integer controlNum = appControlOrderMapper.selectCount(new LambdaQueryWrapper<AppControlOrder>()
                .eq(AppControlOrder::getAppId, req.getAppId())
                .in(AppControlOrder::getId, controlOrderIdList));
        if (controlNum != req.getDataReqList().size()) {
            return CommonResult.failed(CommonCodeEnum.DATA_NUM_NOT_EQUALS_CONTROL_NUM);
        }
        //校验必填控件是否全部填写
        for (ApplicationDataReq dataReq : req.getDataReqList()) {
            //校验控件顺序id是否传入
            if (isNullOrEmpty(dataReq.getControlOrderId())) {
                return CommonResult.failed(CommonCodeEnum.INVALID_PARAM);
            }
            //校验各控件数据是否正确
            if (!isNullOrEmpty(dataReq.getData())) {
                //组织id需为数字
                if (dataReq.getType().equals(CONDITION_SUBTYPE_ORGANIZATION)) {
                    Matcher isNum = Pattern.compile("[0-9]*").matcher(dataReq.getData());
                    if (!isNum.matches()) {
                        return CommonResult.failed(CommonCodeEnum.ORGANIZATION_ID_NOT_NUM);
                    }
                }
            }
            //必填控件必须有值
            Boolean isRequired = appDynamicUtil.getIsRequired(dataReq.getControlOrderId());
            if (isRequired == null) {
                return CommonResult.failed(CommonCodeEnum.CONTROL_NOT_EXIST);
            }
            if (isRequired) {
                if (isNullOrEmpty(dataReq.getData())) {
                    return CommonResult.failed(CommonCodeEnum.REQUIRED_CONTROL_NOT_FILL);
                }
            }
        }
        //校验节点集合
        for (SubmitApplicationDetailReq nodeReq : req.getNodeList()) {
            if (isNullOrEmpty(nodeReq.getProcessNodeId(), nodeReq.getName(), nodeReq.getType(), nodeReq.getSort(),
                    nodeReq.getApproverType(), nodeReq.getApproverObjectId(), nodeReq.getAuthority(), nodeReq.getIsDeliver(),
                    nodeReq.getIsRollback(), nodeReq.getIsSign(), nodeReq.getIsStamp(), nodeReq.getIsOpinion())) {
                return CommonResult.failed(CommonCodeEnum.INVALID_PARAM);
            }
            //审批人类型只可为角色或指定人
            if (nodeReq.getApproverType() != DYNAMIC_APP_ROLE && nodeReq.getApproverType() != DYNAMIC_APP_ASSIGNER) {
                return CommonResult.failed(CommonCodeEnum.APPROVER_TYPE_NOT_ILLEGAL);
            }
        }
        //校验传入节点数量
        List<Integer> sortList = req.getNodeList().stream().map(SubmitApplicationDetailReq::getSort).collect(Collectors.toList());
        Integer maxSort = Collections.max(sortList);
        if (maxSort != sortList.size() - 1) {
            return CommonResult.failed(CommonCodeEnum.PROCESS_NODE_NUM_EXCEPTION);
        }

        //创建申请表
        AppApplication appApplication = new AppApplication();
        appApplication.setAppId(req.getAppId());
        appApplication.setAllowStatus(APPLICATION_ALLOW_STATUS_WAIT_ALLOW);
        appApplication.setUserId(userId);
        appApplicationMapper.insert(appApplication);

        //存储申请表数据
        ArrayList<AppApplicationData> appApplicationDataList = new ArrayList<>();
        for (ApplicationDataReq applicationDataReq : req.getDataReqList()) {
            AppApplicationData applicationData = new AppApplicationData();
            BeanUtils.copyProperties(applicationDataReq, applicationData);
            applicationData.setApplicationId(appApplication.getId());
            appApplicationDataList.add(applicationData);
        }
        this.saveBatch(appApplicationDataList);

        //创建审批流程
        ArrayList<AppProcessNodeDetail> nodeDetailList = new ArrayList<>();
        for (SubmitApplicationDetailReq nodeReq : req.getNodeList()) {
            //拷贝前端传入数据
            AppProcessNodeDetail nodeDetail = new AppProcessNodeDetail();
            BeanUtils.copyProperties(nodeReq, nodeDetail);
            //设置申请人节点审批时间
            if (nodeDetail.getType() == APPLICANT_NODE) {
                nodeDetail.setAllowTime(new Date());
            }
            nodeDetail.setAppId(req.getAppId());
            nodeDetail.setAuthority(JSON.toJSONString(nodeReq.getAuthority()));
            //设置流程初始数据
            if (nodeReq.getType() == APPLICANT_NODE) {//提交人节点直接设置为通过
                nodeDetail.setAllowType(NODE_ALLOW_STATUS_PASS);
            } else {//其他设置为待审批
                nodeDetail.setAllowType(NODE_ALLOW_STATUS_WAIT_ALLOW);
            }
            nodeDetail.setApplicationId(appApplication.getId());

            nodeDetailList.add(nodeDetail);
        }
        appProcessNodeDetailService.saveBatch(nodeDetailList);

        //消息通知
        //向后通知流程节点
        AppProcessNodeDetail applicant = appProcessNodeDetailMapper.selectList(new LambdaQueryWrapper<AppProcessNodeDetail>()
                .eq(AppProcessNodeDetail::getApplicationId, appApplication.getId())
                .eq(AppProcessNodeDetail::getType, APPLICANT_NODE)
                .eq(AppProcessNodeDetail::getSort, 0)).get(0);
        appDynamicUtil.notice(appDynamicUtil.getNextNodeDetail(applicant), userId, appApplication.getId(), SUBMIT_APPLICATION_NOTICE);

        return CommonResult.success();
    }

    @Override
    public ResponseResult selectAllAppApply(Integer pageNum, Integer pageSize, String appId, String applyUser, String searchStr) {
        //校验app是否存在
        AppBasicInfo appBasicInfo = appBasicInfoMapper.selectById(appId);
        if (isNullOrEmpty(appBasicInfo)) {
            return CommonResult.failed(CommonCodeEnum.DYNAMIC_APP_NOT_EXIST);
        }
        //构造查询条件
        List<AppControlOrder> appControlOrderList = appControlOrderMapper.selectList(new LambdaQueryWrapper<AppControlOrder>()
                .eq(AppControlOrder::getAppId, appId)
                .eq(AppControlOrder::getSort, 1));
        if (appControlOrderList.isEmpty()) {
            return CommonResult.failed(CommonCodeEnum.CONTROL_NOT_ADD);
        }
        AppControlOrder controlOrder = appControlOrderList.get(0);
        LambdaQueryWrapper<AppApplication> queryWrapper = new LambdaQueryWrapper<AppApplication>()
                .eq(AppApplication::getAppId, appId)
                .ge(AppApplication::getCreateTime, controlOrder.getUpdateTime())
                .orderByDesc(AppApplication::getCreateTime);

        if (!isNullOrEmpty(applyUser) || !applyUser.equals("")) {
            //去掉字符串中的空格
            StringUtils.deleteWhitespace(applyUser);
            //查询申请表
            List<AppApplication> applicationList = appApplicationMapper.selectList(queryWrapper);
            List<AppApplication> filterApplicationList = applicationList.stream().filter(application -> {
                SmsUser user = smsUserMapper.selectById(application.getUserId());
                //未查到申请人不收集
                if (isNullOrEmpty(user)) {
                    return false;
                }
                //申请人名字不包含
                if (!user.getRealName().contains(applyUser)) {
                    return false;
                }
                return true;
            }).collect(Collectors.toList());
            //添加查询条件
            List<String> applicationIdList = filterApplicationList.stream().map(AppApplication::getId).collect(Collectors.toList());
            if (!applicationIdList.isEmpty()) {
                queryWrapper.in(AppApplication::getId, applicationIdList);
            } else {
                queryWrapper.in(AppApplication::getId, "");
            }
        }

        //模糊查询
        if (!isNullOrEmpty(searchStr) || !searchStr.equals("")) {
            //去掉字符串中的空格
            StringUtils.deleteWhitespace(searchStr);
            //查询申请表
            List<AppApplication> applicationList = appApplicationMapper.selectList(queryWrapper);
            List<AppApplication> filterApplicationList = applicationList.stream().filter(application -> {
                //获取申请表数据
                List<AppApplicationData> dataList = appApplicationDataMapper.selectList(new LambdaQueryWrapper<AppApplicationData>()
                        .eq(AppApplicationData::getApplicationId, application.getId()));
                dataList = dataList.stream().filter(data -> data.getData().contains(searchStr)).collect(Collectors.toList());
                //数据不包含 searchStr 则过滤掉
                return !dataList.isEmpty();
            }).collect(Collectors.toList());
            //添加查询条件
            List<String> applicationIdList = filterApplicationList.stream().map(AppApplication::getId).collect(Collectors.toList());
            if (!applicationIdList.isEmpty()) {
                queryWrapper.in(AppApplication::getId, applicationIdList);
            } else {
                queryWrapper.in(AppApplication::getId, "");
            }
        }

        //构造分页数据
        Page<AppApplication> page = new Page<>(pageNum, pageSize);
        appApplicationMapper.selectPage(page, queryWrapper);

        //构造返回数据
        List<AppApplicationResp> collect = page.getRecords().stream().map(appApplication -> {
            AppApplicationResp appApplicationResp = new AppApplicationResp();
            BeanUtils.copyProperties(appApplication, appApplicationResp);
            //设置申请人信息
            if (!isNullOrEmpty(appApplication.getUserId())) {
                SmsUser userInfo = cacheUtil.getUserInfo(appApplication.getUserId());
                AppApplicationUserResp userResp = new AppApplicationUserResp();
                BeanUtils.copyProperties(userInfo, userResp);
                appApplicationResp.setApplyUserInfo(userResp);
            }
            //设置申请表数据
            List<ApplicationDataResp> dataRespList = appDynamicUtil.getDataRespList(appApplication.getId());
            appApplicationResp.setDataList(dataRespList);

            //设置节点数据
//            List<AppProcessNodeDetail> nodeDetailList = appProcessNodeDetailMapper.selectList(new LambdaQueryWrapper<AppProcessNodeDetail>()
//                    .eq(AppProcessNodeDetail::getApplicationId, appApplication.getId())
//                    .orderByAsc(AppProcessNodeDetail::getSort));
//            List<AppProcessNodeDetailResp> detailRespList = nodeDetailList.stream().map(nodeDetail -> {
//                AppProcessNodeDetailResp nodeDetailResp = new AppProcessNodeDetailResp();
//                BeanUtils.copyProperties(nodeDetail, nodeDetailResp);
//                nodeDetailResp.setAuthority(JSONObject.parseArray(nodeDetail.getAuthority(), AuthorityResp.class));
//                if (!isNullOrEmpty(nodeDetail.getAllowUserId())) {
//                    SmsUser userInfo = cacheUtil.getUserInfo(appApplicationResp.getAllowUserId());
//                    AppApplicationUserResp userResp = new AppApplicationUserResp();
//                    BeanUtils.copyProperties(userInfo, userResp);
//                    nodeDetailResp.setAllowUserInfo(userResp);
//                }
//                return nodeDetailResp;
//            }).collect(Collectors.toList());
//            appApplicationResp.setDetailList(detailRespList);

            return appApplicationResp;
        }).collect(Collectors.toList());

        return CommonResult.success(collect, (int) page.getTotal());
    }

    @Override
    @Transactional
    public ResponseResult forceArchive(ApplicationBackReq backReq, String userId) {
        //校验参数
        if (isNullOrEmpty(backReq.getApplicationId())) {
            return CommonResult.failed(CommonCodeEnum.INVALID_PARAM);
        }
        //校验申请表是否存在
        AppApplication application = appApplicationMapper.selectById(backReq.getApplicationId());
        if (isNullOrEmpty(application)) {
            return CommonResult.failed(CommonCodeEnum.APPLICATION_NOT_EXIST);
        }
        if (application.getAllowStatus() != APPLICATION_ALLOW_STATUS_WAIT_ALLOW) {
            return CommonResult.failed(CommonCodeEnum.APPLICATION_CANNOT_FORCE_ARCHIVE);
        }

        //修改该申请表其他流程节点的状态
        AppProcessNodeDetail nodeDetail = new AppProcessNodeDetail();
        nodeDetail.setAllowUserId(userId);
        nodeDetail.setAllowType(NODE_ALLOW_STATUS_FORCE_PASS);
        nodeDetail.setAllowTime(new Date());
        appProcessNodeDetailMapper.update(nodeDetail, new LambdaQueryWrapper<AppProcessNodeDetail>()
                .eq(AppProcessNodeDetail::getApplicationId, application.getId())
                .eq(AppProcessNodeDetail::getAllowType, NODE_ALLOW_STATUS_WAIT_ALLOW));

        //修改申请表状态
        application.setAllowStatus(APPLICATION_ALLOW_STATUS_PASS);
        appApplicationMapper.updateById(application);

        //通知
        newTipsUtil.cancelFinishTips(backReq.getApplicationId());

        return CommonResult.success();
    }

    @Override
    @Async
    public Future<ResponseResult> exportAllApplyList(AppDynamicExportReq exportReq, HttpServletResponse response) {
        List<ControlInfoResp> controlInfoList = exportReq.getControlInfoList();
        //校验参数
        if (isNullOrEmpty(exportReq.getAppId(), exportReq.getIsIncludeApplicant(), exportReq.getIsExportProcess(), controlInfoList)) {
            return new AsyncResult<>(CommonResult.failed(CommonCodeEnum.INVALID_PARAM));
        }
        //校验应用是否存在
        AppBasicInfo appBasicInfo = appBasicInfoMapper.selectById(exportReq.getAppId());
        if (isNullOrEmpty(appBasicInfo)) {
            return new AsyncResult<>(CommonResult.failed(CommonCodeEnum.DYNAMIC_APP_NOT_EXIST));
        }

        //根据前端传参构造查询条件
        LambdaQueryWrapper<AppApplication> queryWrapper = new LambdaQueryWrapper<AppApplication>()
                .eq(AppApplication::getAppId, exportReq.getAppId())
                .orderByDesc(AppApplication::getCreateTime);
        //加入 审批状态 条件
        if (!isNullOrEmpty(exportReq.getAllowStatus())) {
            queryWrapper.eq(AppApplication::getAllowStatus, exportReq.getAllowStatus());
        }
        //加入 申请人id集合 条件
        if (exportReq.getApplyUserIdList() != null && !exportReq.getApplyUserIdList().isEmpty()) {
            queryWrapper.in(AppApplication::getUserId, exportReq.getApplyUserIdList());
        }
        //查询所有申请表信息
        List<AppApplication> applicationList = appApplicationMapper.selectList(queryWrapper);
        if (applicationList.isEmpty()) {
            return new AsyncResult<>(CommonResult.failed(CommonCodeEnum.LEGAL_APPLICATION_LIST_NOT_EXIST));
        }
        //控件信息集合根据 sort 排序
        controlInfoList.sort(new Comparator<ControlInfoResp>() {
            @Override
            public int compare(ControlInfoResp o1, ControlInfoResp o2) {
                return o1.getSort().compareTo(o2.getSort());
            }
        });
        List<String> controlIdList = controlInfoList.stream().map(ControlInfoResp::getId).collect(Collectors.toList());

        try {
            //声明一个工作簿
            HSSFWorkbook workbook = new HSSFWorkbook();
            //获取sheet页数
            double sheetNum = Math.ceil((double) applicationList.size() / PER_SHEET_NUM);
            for (int k = 0; k < sheetNum; k++) {
                //生成一个 sheet页,设置 sheet页 名称为 "xxx(应用)申请记录导出表"
                HSSFSheet sheet = workbook.createSheet(appBasicInfo.getAppName() + "申请记录导出表" + (k + 1));
                //设置表格列宽度为 12
                sheet.setDefaultColumnWidth(12);

                //设置单元格的显示样式
                HSSFCellStyle cellStyle = workbook.createCellStyle();
                cellStyle.setFillForegroundColor(IndexedColors.YELLOW.index);
                cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
                //设置单元格字体
                HSSFFont font = workbook.createFont();
                font.setFontHeightInPoints((short) 10);//字号
                font.setBold(true);//加粗
                cellStyle.setFont(font);

                //创建第一行表头
                HSSFRow headRow = sheet.createRow(0);
                //设置第一行数据
                int controlSize = controlInfoList.size();
                for (int i = 0; i < controlSize + 1; i++) {
                    //创建一个单元格
                    HSSFCell cell = headRow.createCell(i);
                    cell.setCellStyle(cellStyle);

                    //第一个单元格
                    if (i == 0) {
                        //设置序号
                        cell.setCellValue(new HSSFRichTextString("序号"));
                        continue;
                    }

                    //创建一个内容对象
                    HSSFRichTextString text = new HSSFRichTextString(controlInfoList.get(i - 1).getName());
                    //将内容对象的文字内容写入到单元格中
                    cell.setCellValue(text);
                }

                int gap = 1;
                //判断是否需要设置申请人
                if (exportReq.getIsIncludeApplicant()) {
                    //创建一个单元格
                    HSSFCell cell = headRow.createCell(controlSize + 1);
                    cell.setCellStyle(cellStyle);
                    //创建一个内容对象 并 将内容对象的文字内容写入到单元格中
                    cell.setCellValue(new HSSFRichTextString("申请人"));
                    gap++;
                }

                //判断是否需要添加流程表头
                if (exportReq.getIsExportProcess()) {
                    //创建一个单元格
                    HSSFCell cell = headRow.createCell(controlSize + gap);
                    cell.setCellStyle(cellStyle);
                    cell.setCellValue(new HSSFRichTextString("审批流程"));
                }

                //设置内容
                int range = PER_SHEET_NUM;
                //如果是最后一个 sheet 页, 修改 range 值
                if (k + 1 == sheetNum) {
                    range = applicationList.size() - PER_SHEET_NUM * k;
                }
                for (int i = 0; i < range; i++) {
                    //设置数据
                    AppApplication application = applicationList.get(PER_SHEET_NUM * k + i);
                    //获取当前应用所需导出的控件id集合
                    List<ApplicationDataResp> dataRespList = appDynamicUtil.getDataRespList(application.getId(), controlIdList);
                    if (dataRespList.isEmpty()) {
                        continue;
                    }
                    //创建一行
                    HSSFRow row = sheet.createRow(i + 1);
                    //设置序号
                    row.createCell(0).setCellValue(new HSSFRichTextString(String.valueOf(i + 1)));
                    //申请表数据 根据sort排序
                    dataRespList.sort(new Comparator<ApplicationDataResp>() {
                        @Override
                        public int compare(ApplicationDataResp o1, ApplicationDataResp o2) {
                            return o1.getSort().compareTo(o2.getSort());
                        }
                    });
                    //设置该行数据
                    for (int j = 0; j < dataRespList.size(); j++) {
                        String data = "";
                        if (!isNullOrEmpty(dataRespList.get(j).getData())) {
                            ApplicationDataResp dataResp = dataRespList.get(j);
                            switch (dataResp.getType()) {
                                case CONDITION_SUBTYPE_ORGANIZATION://组织展示组织名
                                    SmsOrgStructure smsOrgStructure = smsOrgStructureMapper.selectById(dataResp.getData());
                                    if (!isNullOrEmpty(smsOrgStructure)) {
                                        data = smsOrgStructure.getOrgName();
                                    }
                                    break;
                                case CONDITION_SUBTYPE_RANGE://时间范围展示格式:[yyyy/mm/dd-yyyy/mm/dd]
                                    //元数据
                                    String oriData = dataResp.getData();
                                    data = oriData.replaceAll("\"", "").replaceAll(",", "-");
                                    break;
                                default://其他
                                    data = dataResp.getData();
                            }
                        }
                        row.createCell(j + 1).setCellValue(new HSSFRichTextString(data));
                    }

                    //需要输出申请人
                    if (exportReq.getIsIncludeApplicant()) {
                        SmsUser userInfo = cacheUtil.getUserInfo(application.getUserId());
                        row.createCell(dataRespList.size() + 1).setCellValue(new HSSFRichTextString(isNullOrEmpty(userInfo) ? "" : userInfo.getRealName()));
                    }

                    //需要输出流程
                    if (exportReq.getIsExportProcess()) {
                        //获取该申请表所有流程
                        List<AppProcessNodeDetail> detailList = appProcessNodeDetailMapper.selectList(new LambdaQueryWrapper<AppProcessNodeDetail>()
                                .eq(AppProcessNodeDetail::getApplicationId, application.getId())
                                .orderByAsc(AppProcessNodeDetail::getSort));
                        StringBuilder stringBuilder = new StringBuilder();
                        if (!detailList.isEmpty()) {
                            int sort = 0;
                            for (AppProcessNodeDetail detail : detailList) {
                                //审批状态不为 通过 或 驳回 则不收集
                                if (detail.getAllowType() != NODE_ALLOW_STATUS_PASS && detail.getAllowType() != NODE_ALLOW_STATUS_DENY
                                        && detail.getAllowType() != NODE_ALLOW_STATUS_FORCE_PASS && detail.getAllowType() != NODE_ALLOW_STATUS_FORCE_REJECT) {
                                    break;
                                }
                                SmsUser smsUser = smsUserMapper.selectById(detail.getAllowUserId());
                                if (sort == 0) {
                                    if (smsUser == null) {
                                        stringBuilder.append("提交人：").append("\n");
                                    } else {
                                        stringBuilder.append("提交人：").append(smsUser.getRealName()).append("\n");
                                    }
                                    if (isNullOrEmpty(detail.getAllowTime())) {
                                        stringBuilder.append("提交时间：").append("\n");
                                    } else {
                                        stringBuilder.append("提交时间：").append(TimeUtil.DateHanZiSimpleFormat(detail.getAllowTime())).append("\n").append("\n");
                                    }
                                } else {
                                    //设置流程信息
                                    stringBuilder.append(sort).append(":").append(detail.getName()).append(":\n");
                                    if (smsUser == null) {
                                        stringBuilder.append("审批人：").append("\n");
                                    } else {
                                        stringBuilder.append("审批人：").append(smsUser.getRealName()).append("\n");
                                    }
                                    if (detail.getAllowType() == NODE_ALLOW_STATUS_PASS || detail.getAllowType() == NODE_ALLOW_STATUS_FORCE_PASS) {
                                        stringBuilder.append("审批状态：同意").append("\n");
                                    } else {
                                        stringBuilder.append("审批状态：拒绝").append("\n");
                                    }
                                    if (isNullOrEmpty(detail.getOpinion())) {
                                        stringBuilder.append("审批意见：").append("\n");
                                    } else {
                                        stringBuilder.append("审批意见：").append(detail.getOpinion()).append("\n");
                                    }
                                    if (isNullOrEmpty(detail.getAllowTime())) {
                                        stringBuilder.append("审批时间：").append("\n");
                                    } else {
                                        stringBuilder.append("审批时间：").append(TimeUtil.DateHanZiSimpleFormat(detail.getAllowTime())).append("\n").append("\n");
                                    }
                                }
                                sort++;
                            }
                        }
                        row.createCell(controlSize + gap).setCellValue(new HSSFRichTextString(stringBuilder.toString()));
                    }
                }
            }

            //设置 response
            //response.reset();
            response.setContentType("application/vnd.ms-excel;charset=utf-8");
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(appBasicInfo.getAppName() + "申请记录导出表.xlsx", "utf-8"));
            response.setCharacterEncoding("utf-8");
            response.setHeader("file-type", "download");
            response.setHeader("file-name", URLEncoder.encode(appBasicInfo.getAppName() + "申请记录导出表.xlsx", "utf-8"));
            response.setHeader("Access-Control-Expose-Headers", "file-type,file-name");
            //刷新缓冲
            response.flushBuffer();
            //workbook将Excel写入到response的输出流中，供页面下载
            OutputStream os = response.getOutputStream();
            workbook.write(os);
            workbook.close();
            os.close();
        } catch (Exception e) {
            e.printStackTrace();
            return new AsyncResult<>(CommonResult.failed(CommonCodeEnum.FAIL));
        }
        return null;
    }

    @Override
    public ResponseResult getControlInfo(String appId) {
        //校验应用id
        AppBasicInfo appBasicInfo = appBasicInfoMapper.selectById(appId);
        if (isNullOrEmpty(appBasicInfo)) {
            return CommonResult.failed(CommonCodeEnum.DYNAMIC_APP_NOT_EXIST);
        }

        List<ControlInfoResp> controlInfoRespList = appDynamicUtil.getControlInfoList(appId, null);
        return CommonResult.success(controlInfoRespList, controlInfoRespList.size());
    }

    @Override
    @Async
    public Future<ResponseResult> tableDownload(String applyId, HttpServletResponse response) {
        //校验申请表是否存在
        AppApplication apply = appApplicationMapper.selectById(applyId);
        if (isNullOrEmpty(apply)) {
            return new AsyncResult<>(CommonResult.failed(CommonCodeEnum.APPLICATION_NOT_EXIST));
        }
        //获取当前应用信息
        AppBasicInfo appBasicInfo = appBasicInfoMapper.selectById(apply.getAppId());

        //根据系统拼接下载路径
        String filePath = System.getProperty("os.name").toLowerCase().startsWith("win") ? fileSavePathConstant.getFILE_DOWNLOAD_PATH()
                : fileSavePathConstant.getFILE_DOWNLOAD_PATH_LINUX();
        String docFilePath = filePath + applyId + ".docx";
        String pdfFilePath = filePath + applyId + ".pdf";

        try {
            //构造存放数据的Map
            Map<String, Object> templateMap = new HashMap<>();
            //图片类型的数据需进行处理
            List<String> picTagList = new ArrayList<>();
            //设置 applyTitle
            String applyTitle = appBasicInfo.getAppName() + "申请表";
            templateMap.put("applyTitle", applyTitle);
            //获取用户信息,设置 applyUser
            SmsUser user = smsUserMapper.selectById(apply.getUserId());
            if (user != null) {
                templateMap.put("applyUser", user.getRealName());
            }
            templateMap.put("createTime", TimeUtil.DateHanZiSimpleFormat(apply.getCreateTime()));

            //设置申请表中的数据
            List<ApplicationDataResp> dataRespList = appDynamicUtil.getDataRespList(applyId);
            for (ApplicationDataResp dataResp : dataRespList) {
                templateMap.put(String.valueOf(dataResp.getSort()), isNullOrEmpty(dataResp.getData()) ? "" : dataResp.getData());
            }

            //获取审批流程
            List<AppProcessNodeDetail> nodeDetailList = appProcessNodeDetailMapper.selectList(new LambdaQueryWrapper<AppProcessNodeDetail>()
                    .eq(AppProcessNodeDetail::getApplicationId, applyId)
                    .orderByAsc(AppProcessNodeDetail::getSort));
            if (!nodeDetailList.isEmpty()) {
                for (AppProcessNodeDetail process : nodeDetailList) {
                    String sort = String.valueOf(process.getSort());
                    if (process.getAllowType() == NODE_ALLOW_STATUS_PASS || process.getAllowType() == NODE_ALLOW_STATUS_DENY
                            || process.getAllowType() == NODE_ALLOW_STATUS_FORCE_PASS || process.getAllowType() == NODE_ALLOW_STATUS_FORCE_REJECT) {
                        //获取审批人信息 设置 审批人姓名
                        SmsUser smsUser = smsUserMapper.selectById(process.getAllowUserId());
                        if (smsUser != null && isNullOrEmpty(smsUser.getRealName())) {
                            templateMap.put("allowUser" + sort, smsUser.getRealName());
                        }

                        //设置审批状态
                        if (process.getAllowType() == NODE_ALLOW_STATUS_PASS || process.getAllowType() == NODE_ALLOW_STATUS_FORCE_PASS) {
                            templateMap.put("allowStatus" + sort, "同意");
                        } else {
                            templateMap.put("allowStatus" + sort, "拒绝");
                        }

                        //设置签名
                        if (isNullOrEmpty(process.getSign())) {
                            templateMap.put("allowSign" + sort, "");
                        } else {
                            String[] splitStr = process.getSign().split("=");
                            String fileName = splitStr[splitStr.length - 1];
                            File file = new File(filePath + fileName);
                            if (file.exists()) {
                                InputStream inputStream = Files.newInputStream(file.toPath());
                                PictureRenderData picture = Pictures.ofStream(inputStream, PictureType.PNG).size(60, 30).create();

                                templateMap.put("allowSign" + sort, picture);
                                picTagList.add("allowSign" + sort);
                            }
                        }
                        //设置印章
                        if (isNullOrEmpty(process.getStamp())) {
                            templateMap.put("allowStamp" + sort, "");
                        } else {
                            String[] splitStr = process.getStamp().split("=");
                            String fileName = splitStr[splitStr.length - 1];
                            File file = new File(filePath + fileName);
                            if (file.exists()) {
                                InputStream inputStream = Files.newInputStream(file.toPath());
                                PictureRenderData picture = Pictures.ofStream(inputStream, PictureType.PNG).size(60, 30).create();
                                templateMap.put("allowStamp" + sort, picture);
                                picTagList.add("allowStamp" + sort);
                            }
                        }

                        //设置意见
                        if (!isNullOrEmpty(process.getOpinion())) {
                            templateMap.put("allowOpinion" + sort, process.getOpinion());
                        } else {
                            templateMap.put("allowOpinion" + sort, "");
                        }
                        //设置审批时间
                        if (!isNullOrEmpty(process.getAllowTime())) {
                            templateMap.put("allowTime" + sort, TimeUtil.DateHanZiSimpleFormat(process.getAllowTime()));
                        } else {
                            templateMap.put("allowTime" + sort, "");
                        }
                    }
                }
            }

            //获取模板名
            List<AppTemplate> templateList = appTemplateMapper.selectList(new LambdaQueryWrapper<AppTemplate>().eq(AppTemplate::getAppId, apply.getAppId()));
            if (templateList.isEmpty()) {
                return new AsyncResult<>(CommonResult.failed(CommonCodeEnum.TEMPLATE_NOT_EXIST));
            }
            String dynamicFilePath = System.getProperty("os.name").toLowerCase().startsWith("win") ? fileSavePathConstant.getDYNAMIC_FILE_DOWNLOAD_PATH()
                    : fileSavePathConstant.getDYNAMIC_FILE_DOWNLOAD_PATH_LINUX();
            File file = new File(dynamicFilePath + templateList.get(0).getTemplateName());
            if (!file.exists()) {
                return new AsyncResult<>(CommonResult.failed(CommonCodeEnum.DYNAMIC_APP_TEMPLATE_NOT_EXIST));
            }
            InputStream is = Files.newInputStream(file.toPath());
            XWPFTemplate template = XWPFTemplate.compile(is);

            //处理图片配置
            Configure config = template.getConfig();
            for (String picTag : picTagList) {
                config.customPolicy(picTag, new PictureRenderPolicy());
            }
            template.render(templateMap);
            FileOutputStream outputStream = new FileOutputStream(docFilePath);
            template.write(outputStream);

            //word 转 pdf
            FileInputStream fileInputStream = new FileInputStream(docFilePath);
            FileOutputStream fileOutputStream = new FileOutputStream(pdfFilePath);
            Document document = new Document();
            document.loadFromStream(fileInputStream, FileFormat.Docx);
            //保存为PDF
            document.saveToFile(pdfFilePath, FileFormat.PDF);

            is.close();
            outputStream.close();
            template.close();
            fileInputStream.close();
            fileOutputStream.close();
            document.close();
        } catch (Exception e) {
            e.printStackTrace();
            return new AsyncResult<>(CommonResult.failed(CommonCodeEnum.FAIL));
        }

        try {
            //创建文件
            File pdfFile = new File(pdfFilePath);
            // 加载PDF文档
            PDDocument doc = PDDocument.load(pdfFile);
            doc.setAllSecurityToBeRemoved(true);
            // 创建水印
            String watermark = "";
            PDPageContentStream contentStream;
            for (PDPage page : doc.getPages()) {
                contentStream = new PDPageContentStream(doc, page, PDPageContentStream.AppendMode.APPEND, true, true);

                //加载字体
                ClassPathResource resource = new ClassPathResource("fonts/123.ttf");
                InputStream is = resource.getInputStream();
                PDFont font = PDType0Font.load(doc, is);

                int startX = 0; // 起始位置 x 坐标
                int startY = 0; // 起始位置 y 坐标
                float angle = -45; // 角度
                float interval = 220; // 间隔
                float opacity = 0.3f; // 透明度
                int colorR = 127; // 颜色 red 值
                int colorG = 127; // 颜色 green 值
                int colorB = 127; // 颜色 blue 值

                //透明的
                PDExtendedGraphicsState r0 = new PDExtendedGraphicsState();
                r0.setNonStrokingAlphaConstant(opacity);
                r0.setAlphaSourceFlag(true);
                contentStream.setGraphicsStateParameters(r0);

                //水印颜色
                contentStream.setNonStrokingColor(colorR, colorG, colorB);
                contentStream.beginText();
                contentStream.setFont(font, 30f);

                int xTimes = (int) Math.ceil(page.getMediaBox().getWidth() / interval);
                int yTimes = (int) Math.ceil(page.getMediaBox().getHeight() / interval);
                for (int x = startX; x <= xTimes; x++) {
                    for (int y = startY; y <= yTimes; y++) {
                        contentStream.setTextRotation(angle, (x * interval), (y * interval));
                        contentStream.showText(watermark);
                    }
                }
                contentStream.endText();
                contentStream.restoreGraphicsState();
                contentStream.close();
                is.close();
            }

//            response.reset();
            response.setCharacterEncoding("utf-8");
            response.setContentType("application/vnd.pdf;charset=utf-8");
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(appBasicInfo.getAppName() + "申请表预览.pdf", "utf-8"));
            response.setHeader("file-type", "download");
            response.setHeader("file-name", URLEncoder.encode(appBasicInfo.getAppName() + "申请表预览.pdf", "utf-8"));
            response.setHeader("Access-Control-Expose-Headers", "file-type,file-name");
            //刷新缓冲
            response.flushBuffer();

            //保存PDF文档
            doc.save(response.getOutputStream());
            doc.close();
        } catch (Exception e) {
            e.printStackTrace();
            return new AsyncResult<>(CommonResult.failed(CommonCodeEnum.FAIL));
        }
        return null;
    }

    @Override
    @Transactional
    public ResponseResult forceExchange(ApplicationTransmitReq transmitReq) {
        if (isNullOrEmpty(transmitReq.getApplicationId(), transmitReq.getProcessNodeDetailId(), transmitReq.getUserId())) {
            return CommonResult.failed(CommonCodeEnum.INVALID_PARAM);
        }
        //校验申请表是否存在
        AppApplication application = appApplicationMapper.selectById(transmitReq.getApplicationId());
        if (isNullOrEmpty(application)) {
            return CommonResult.failed(CommonCodeEnum.APPLICATION_NOT_EXIST);
        }
        //校验节点是否存在
        AppProcessNodeDetail nodeDetail = appProcessNodeDetailMapper.selectById(transmitReq.getProcessNodeDetailId());
        if (isNullOrEmpty(nodeDetail)) {
            return CommonResult.failed(CommonCodeEnum.NODE_DETAIL_NOT_EXIST);
        }
        if (nodeDetail.getAllowType() != NODE_ALLOW_STATUS_WAIT_ALLOW) {
            return CommonResult.failed(CommonCodeEnum.NODE_DETAIL_CAN_NOT_FORCE_EXCHANGE);
        }
        //校验审批人是否存在
        SmsUser user = smsUserMapper.selectById(transmitReq.getUserId());
        if (isNullOrEmpty(user)) {
            return CommonResult.failed(CommonCodeEnum.DYNAMIC_APP_USER_NOT_EXIST);
        }

        //更新审批人
        nodeDetail.setApproverType(DYNAMIC_APP_ASSIGNER);
        nodeDetail.setApproverObjectId(transmitReq.getUserId());
        appProcessNodeDetailMapper.updateById(nodeDetail);

        //消息通知
        AppProcessNodeDetail prevNodeDetail = appDynamicUtil.getPrevNodeDetail(nodeDetail);
        //存在前一个节点且前一个节点为通过
        if (!isNullOrEmpty(prevNodeDetail) && prevNodeDetail.getAllowType() == NODE_ALLOW_STATUS_PASS) {
            appDynamicUtil.notice(nodeDetail, transmitReq.getUserId(), transmitReq.getApplicationId(), TRANS_OR_BACK_APPLICATION_NOTICE);
        }
        return CommonResult.success();
    }

    @Override
    @Transactional
    public ResponseResult insteadAllow(ApplicationAllowReq allowReq, String userId) {
        if (isNullOrEmpty(allowReq.getApplicationId(), allowReq.getProcessNodeDetailId(), allowReq.getAllowType())) {
            return CommonResult.failed(CommonCodeEnum.INVALID_PARAM);
        }
        //审批结果不为 通过/驳回
        if (allowReq.getAllowType() != NODE_ALLOW_STATUS_PASS && allowReq.getAllowType() != NODE_ALLOW_STATUS_DENY) {
            return CommonResult.failed(CommonCodeEnum.INVALID_PARAM);
        }
        //校验申请表是否存在
        AppApplication application = appApplicationMapper.selectById(allowReq.getApplicationId());
        if (isNullOrEmpty(application)) {
            return CommonResult.failed(CommonCodeEnum.APPLICATION_NOT_EXIST);
        }
        //校验节点
        AppProcessNodeDetail nodeDetail = appProcessNodeDetailMapper.selectById(allowReq.getProcessNodeDetailId());
        if (isNullOrEmpty(nodeDetail) || nodeDetail.getAllowType() != NODE_ALLOW_STATUS_WAIT_ALLOW) {
            return CommonResult.failed(CommonCodeEnum.PROCESS_NODE_DETAIL_ALLOW_FAIL);
        }
        //校验上一个节点状态是否为“通过”
        AppProcessNodeDetail prevNodeDetail = appDynamicUtil.getPrevNodeDetail(nodeDetail);
        if (isNullOrEmpty(prevNodeDetail) || prevNodeDetail.getAllowType() != NODE_ALLOW_STATUS_PASS) {
            return CommonResult.failed(CommonCodeEnum.PROCESS_NODE_DETAIL_ALLOW_FAIL);
        }

        //修改ProcessDetail节点表
        AppProcessNodeDetail node = new AppProcessNodeDetail();
        node.setId(allowReq.getProcessNodeDetailId());
        node.setAllowType(allowReq.getAllowType());
        node.setAllowUserId(userId);
        node.setAllowTime(new Date());
        //设置签名
        if (nodeDetail.getIsSign()) {
            if (isNullOrEmpty(allowReq.getSign())) {
                return CommonResult.failed(CommonCodeEnum.INVALID_PARAM);
            }
            node.setSign(allowReq.getSign());
        }
//        //设置印章
//        if (nodeDetail.getIsStamp()) {
//            if (isNullOrEmpty(allowReq.getStamp())) {
//                return CommonResult.failed(CommonCodeEnum.INVALID_PARAM);
//            }
//            node.setStamp(allowReq.getStamp());
//        }
        //设置审批意见
        if (nodeDetail.getIsOpinion()) {
            if (isNullOrEmpty(allowReq.getOpinion())) {
                return CommonResult.failed(CommonCodeEnum.INVALID_PARAM);
            }
            node.setOpinion(allowReq.getOpinion());
        }
        appProcessNodeDetailMapper.updateById(node);

        //流程是否结束标志
        boolean processIsEnd = false;
        //判断是否最后一个节点/是否驳回
        if (appDynamicUtil.getNextNodeDetail(nodeDetail) == null || allowReq.getAllowType() == NODE_ALLOW_STATUS_DENY) {
            //流程结束
            processIsEnd = true;
            //修改申请表
            AppApplication appApplication = new AppApplication();
            appApplication.setId(allowReq.getApplicationId());
            appApplication.setAllowStatus(allowReq.getAllowType());
            appApplicationMapper.updateById(appApplication);

            //节点驳回 重置其余节点状态
            if (allowReq.getAllowType() == NODE_ALLOW_STATUS_DENY) {
                AppProcessNodeDetail updateDenyDetail = new AppProcessNodeDetail();
                updateDenyDetail.setAllowType(NODE_ALLOW_STATUS_DENIED);
                appProcessNodeDetailMapper.update(updateDenyDetail, new LambdaQueryWrapper<AppProcessNodeDetail>()
                        .eq(AppProcessNodeDetail::getApplicationId, allowReq.getApplicationId())
                        .eq(AppProcessNodeDetail::getAllowType, NODE_ALLOW_STATUS_WAIT_ALLOW));
            }
        }

        //消息通知
        if (processIsEnd) {//流程结束
            boolean allowResult = false;//流程状态
            if (allowReq.getAllowType() == NODE_ALLOW_STATUS_PASS) {//通过
                allowResult = true;

                //特殊应用流程完毕后进行特殊操作
                //获取应用信息
                AppBasicInfo appBasicInfo = appBasicInfoMapper.selectById(application.getAppId());
                specialOperationsAfterCompleting(appBasicInfo.getAppName(), application, userId);
            }
            newTipsUtil.allowFinishTips(allowReq.getApplicationId(), userId, allowResult);
        } else {
            //消息通知
            //向后通知流程节点
            AppProcessNodeDetail nextNode = appDynamicUtil.getNextNodeDetail(nodeDetail);
            appDynamicUtil.notice(nextNode, userId, allowReq.getApplicationId(), ALLOW_APPLICATION_NOTICE);
        }
        return CommonResult.success();
    }

    @Override
    @Transactional
    public ResponseResult forcedReject(ApplicationBackReq backReq, String userId) {
        if (isNullOrEmpty(backReq.getApplicationId(), backReq.getReason())) {
            return CommonResult.failed(CommonCodeEnum.INVALID_PARAM);
        }
        AppApplication application = appApplicationMapper.selectById(backReq.getApplicationId());
        if (isNullOrEmpty(application)) {
            return CommonResult.failed(CommonCodeEnum.APPLICATION_NOT_EXIST);
        }
        //只有待审批状态才可强制驳回
        if (application.getAllowStatus() != APPLICATION_ALLOW_STATUS_WAIT_ALLOW) {
            return CommonResult.failed(CommonCodeEnum.APPLICATION_CANNOT_FORCE_REJECT);
        }

        //修改申请表状态
        application.setAllowStatus(APPLICATION_ALLOW_STATUS_DENY);
        appApplicationMapper.updateById(application);
        //修改节点状态为强制驳回
        AppProcessNodeDetail nodeDetail = new AppProcessNodeDetail();
        nodeDetail.setAllowTime(new Date());
        nodeDetail.setAllowUserId(userId);
        nodeDetail.setAllowType(NODE_ALLOW_STATUS_FORCE_REJECT);
        nodeDetail.setOpinion(backReq.getReason());
        appProcessNodeDetailMapper.update(nodeDetail, new LambdaQueryWrapper<AppProcessNodeDetail>()
                .eq(AppProcessNodeDetail::getApplicationId, backReq.getApplicationId())
                .eq(AppProcessNodeDetail::getAllowType, NODE_ALLOW_STATUS_WAIT_ALLOW));
        //消息提醒
        newTipsUtil.cancelFinishTips(backReq.getApplicationId());
        return CommonResult.success();
    }

    @Override
    @Transactional
    public ResponseResult deleteApplyData(String applyId) {
        AppApplication application = appApplicationMapper.selectById(applyId);
        if (isNullOrEmpty(application)) {
            return CommonResult.failed(CommonCodeEnum.APPLICATION_NOT_EXIST);
        }

        //删除申请表数据
        appApplicationDataMapper.delete(new LambdaQueryWrapper<AppApplicationData>().eq(AppApplicationData::getApplicationId, applyId));
        //删除消息
        List<SmsNewTips> smsNewTipList = smsNewTipsMapper.selectList(new LambdaQueryWrapper<SmsNewTips>().eq(SmsNewTips::getEventId, applyId));
        if (!smsNewTipList.isEmpty()) {
            List<String> newTipsIdList = smsNewTipList.stream().map(SmsNewTips::getId).collect(Collectors.toList());
            smsNewTipsMapper.deleteBatchIds(newTipsIdList);
            smsNewTipsRecordMapper.delete(new LambdaQueryWrapper<SmsNewTipsRecord>().in(SmsNewTipsRecord::getRecordTipsId, newTipsIdList));
        }
        //删除申请表
        appApplicationMapper.deleteById(applyId);

        //删除节点(NodeDetail)
        appProcessNodeDetailMapper.delete(new LambdaQueryWrapper<AppProcessNodeDetail>().eq(AppProcessNodeDetail::getApplicationId, applyId));
        appProcessRollbackRecordMapper.delete(new LambdaQueryWrapper<AppProcessRollbackRecord>().eq(AppProcessRollbackRecord::getAppId, applyId));

        return CommonResult.success();
    }

}
